/**
 * 
 */
package it.uniroma3.engine.indexer;

import it.uniroma3.beans.ProteinName;
import it.uniroma3.beans.ShortName;
import it.uniroma3.engine.factory.DirectoryFactory;
import it.uniroma3.engine.paramer.EngineParameter;
import it.uniroma3.engine.util.Utils;

import java.io.IOException;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.TermVector;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.store.LockObtainFailedException;

/**
 * Indexer: This class model the IndexWriter index 
 * 
 * @author Accosta Augusto
 * @author Lopez Elisabetta
 *
 */
public class Indexer {

	private IndexWriter indexWriter;
	private int numDocs;
	
	/**
	 * Constructor
	 */
	public Indexer() {
		try {
			this.setIndexWriter(getIndexWriter());
			
		} catch (CorruptIndexException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - IndexWriter creation! " + e.getMessage());
			e.printStackTrace();
		} catch (LockObtainFailedException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - IndexWriter creation! " + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - IndexWriter creation! " + e.getMessage());
			e.printStackTrace();
		} 
	}

	/**
	 * This method add the relative protein to the {@code IndexWriter} such as a {@code Document} with the specified {@code Field}s
	 * 
	 * @param proteinName
	 */
	public void indexProtein(String proteinName) {
		Document doc = new Document();
		
		// TODO se calcolo anche gli spelled-out conviene che l'indice me lo salvo su disco una volta per tutte
		String spelledOutProtein = Utils.spelledOut(proteinName);
		
		doc.add(new Field(EngineParameter.CONTENTS_FULLNAME, proteinName, Field.Store.YES, Field.Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
		doc.add(new Field(EngineParameter.CONTENTS_SPELLED_OUT_FULLNAME, spelledOutProtein, Field.Store.YES, Field.Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
		
		try {
			this.indexWriter.addDocument(doc, EngineParameter.ANALYZER);
			
		} catch (CorruptIndexException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - Addind document to index. " + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - Addind document to index. " + e.getMessage());
			e.printStackTrace();
		}
	}
	
	/**
	 * This method add the relative protein to the {@code}IndexWriter such as a {@code}Document with the specified {@code Field}s
	 * 
	 * @param proteinName
	 */
	public void indexProtein(ProteinName proteinName) {
		Document doc = new Document();
		
		String spelledOutProtein = Utils.spelledOut(proteinName.getFullName());
		
		doc.add(new Field("proteinnameid", proteinName.getProteinNameId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
		doc.add(new Field(EngineParameter.CONTENTS_FULLNAME, proteinName.getFullName(), Field.Store.YES, Field.Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
		doc.add(new Field(EngineParameter.CONTENTS_SPELLED_OUT_FULLNAME, spelledOutProtein, Field.Store.YES, Field.Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
		doc.add(new Field("proteinid", proteinName.getProteinId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
		
		try {
			this.indexWriter.addDocument(doc, EngineParameter.ANALYZER);
			
		} catch (CorruptIndexException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - Addind document to index. " + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - Addind document to index. " + e.getMessage());
			e.printStackTrace();
		}
	}
	
	/**
	 * This method add the relative protein to the {@code}IndexWriter such as a {@code}Document with the specified {@code Field}s
	 * 
	 * @param shortName
	 */
	public void indexProtein(ShortName shortName) {
		Document doc = new Document();
		
		String spelledOutProtein = Utils.spelledOut(shortName.getShortName());
		
		doc.add(new Field("shortnameid", shortName.getShortNameId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
		doc.add(new Field(EngineParameter.CONTENTS_FULLNAME, shortName.getShortName(), Field.Store.YES, Field.Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
		doc.add(new Field(EngineParameter.CONTENTS_SPELLED_OUT_FULLNAME, spelledOutProtein, Field.Store.YES, Field.Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS));
		doc.add(new Field("proteinnameid", shortName.getProteinNameId().toString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
		
		try {
			this.indexWriter.addDocument(doc, EngineParameter.ANALYZER);
			
		} catch (CorruptIndexException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - Addind document to index. " + e.getMessage());
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			System.err.println("Fail! - Addind document to index. " + e.getMessage());
			e.printStackTrace();
		}
	}
	
	/**
	 * This method add a new protein name into the index.
	 * This method can be used when a new protein will be inserted in the Oracle DB. In this way,
	 * if you call this method even the index will be updated
	 * 
	 * @param proteinName
	 * @see indexProtein(String proteinName) method
	 */
	public void addNewProteinToExistingIndex(String proteinName) {
		this.indexProtein(proteinName);
	}
	
	/**
	 * This method add a new protein name into the index.
	 * This method can be used when a new protein will be inserted in the Oracle DB. In this way,
	 * if you call this method even the index will be updated
	 * 
	 * @param proteinName
	 * @see indexProtein(ProteinName proteinName) method
	 */
	public void addNewProteinToExistingIndex(ProteinName proteinName) {
		this.indexProtein(proteinName);
	}
	
	/**
	 * This method optimize and close the {@code IndexWriter} 
	 */
	public void close() {
		try {
			this.indexWriter.optimize();
			this.indexWriter.close();
		} catch (CorruptIndexException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * This method retrieve the actual {@code IndexWriter} 
	 * 
	 * @return the indexWriter
	 * @throws IOException 
	 * @throws LockObtainFailedException 
	 * @throws CorruptIndexException 
	 */
	public IndexWriter getIndexWriter() throws CorruptIndexException, LockObtainFailedException, IOException {
		return new IndexWriter(DirectoryFactory.getFSDirectory(), EngineParameter.ANALYZER, true, IndexWriter.MaxFieldLength.UNLIMITED);
	}

	/**
	 * @param indexWriter the indexWriter to set
	 */
	public void setIndexWriter(IndexWriter indexWriter) {
		this.indexWriter = indexWriter;
	}

	/**
	 * @return the numDocs
	 */
	public int getNumDocs() {
		return this.numDocs;
	}
	
	/**
	 * @param numDocs the numDocs to set
	 */
	public void setNumDocs(int numDocs) {
		this.numDocs = numDocs;
	}

}
